"""
余弦相似度计算工具
用于根据频道标签匹配推荐内容
"""
from typing import List, Dict
import numpy as np
from collections import Counter


def cosine_similarity(vec1: Dict, vec2: Dict) -> float:
    """
    计算两个向量的余弦相似度
    
    参数:
        vec1: 第一个向量（字典形式，键为特征，值为权重）
        vec2: 第二个向量（字典形式）
    
    返回:
        相似度分数（0-1之间）
    """
    # 获取所有特征
    features = set(vec1.keys()) | set(vec2.keys())
    
    # 构建向量
    v1 = np.array([vec1.get(f, 0) for f in features])
    v2 = np.array([vec2.get(f, 0) for f in features])
    
    # 计算余弦相似度
    dot_product = np.dot(v1, v2)
    norm1 = np.linalg.norm(v1)
    norm2 = np.linalg.norm(v2)
    
    if norm1 == 0 or norm2 == 0:
        return 0.0
    
    return dot_product / (norm1 * norm2)


def tag_similarity(user_tags: List[str], content_tags: List[str]) -> float:
    """
    基于标签的相似度计算
    
    参数:
        user_tags: 用户选择的频道标签
        content_tags: 内容拥有的标签
    
    返回:
        相似度分数
    """
    if not content_tags:
        return 0.0
    
    # 转换为小写进行匹配
    user_tags_lower = set(tag.lower() for tag in user_tags)
    content_tags_lower = set(tag.lower() for tag in content_tags)
    
    # 计算交集和并集
    intersection = user_tags_lower & content_tags_lower
    union = user_tags_lower | content_tags_lower
    
    if not union:
        return 0.0
    
    # Jaccard相似度
    jaccard_similarity = len(intersection) / len(union)
    
    # 考虑匹配标签的数量
    match_ratio = len(intersection) / max(len(content_tags_lower), 1)
    
    # 综合相似度
    similarity = (jaccard_similarity * 0.6 + match_ratio * 0.4)
    
    return similarity


def rank_content_by_similarity(
    user_channels: List[str],
    all_contents: List,
    limit: int = 20
) -> List:
    """
    根据用户选择的频道，使用相似度排序内容
    
    参数:
        user_channels: 用户选择的频道列表
        all_contents: 所有内容对象
        limit: 返回的内容数量限制
    
    返回:
        按相似度排序的内容列表
    """
    # 为每个内容计算相似度
    content_scores = []
    
    for content in all_contents:
        # 获取内容标签
        if isinstance(content.tags, str):
            import json
            content_tags = json.loads(content.tags) if content.tags else []
        else:
            content_tags = content.tags or []
        
        # 计算相似度
        similarity = tag_similarity(user_channels, content_tags)
        
        content_scores.append({
            'content': content,
            'similarity': similarity
        })
    
    # 按相似度降序排序
    content_scores.sort(key=lambda x: x['similarity'], reverse=True)
    
    # 返回top N内容
    return [item['content'] for item in content_scores[:limit] if item['similarity'] > 0]


def get_channel_tags(channel_name: str) -> List[str]:
    """
    根据频道名称返回相关标签（小写，便于匹配）
    
    参数:
        channel_name: 频道名称
    
    返回:
        标签列表
    """
    channel_tag_map = {
        '推荐': ['美食', '摄影', '旅行', '手工', '美妆'],
        '萌宠': ['萌宠', '宠物', '猫咪'],
        '头像': ['头像', '设计'],
        '美食': ['美食', '探店', '料理'],
        '宠物': ['宠物', '萌宠', '猫咪'],
        '汽车': ['汽车'],
        '游戏': ['游戏', '攻略'],
        '健身': ['健身'],
        '音乐': ['音乐'],
        '舞蹈': ['舞蹈'],
        '旅行': ['旅行', '景色', '摄影'],
        '摄影': ['摄影', '旅行', '景色'],
        '美妆': ['美妆', '化妆'],
        '电影': ['电影'],
        '视频': ['视频'],
        '科技': ['科技'],
        '读书': ['读书'],
        '搞笑': ['搞笑'],
    }
    
    # 返回频道名称本身作为主标签
    tags = [channel_name] + channel_tag_map.get(channel_name, [])
    return tags


def match_channels_to_tags(channels: List[str]) -> List[str]:
    """
    将用户选择的频道转换为标签列表
    
    参数:
        channels: 用户选择的频道列表
    
    返回:
        所有相关的标签列表
    """
    all_tags = []
    for channel in channels:
        tags = get_channel_tags(channel)
        all_tags.extend(tags)
    
    # 去重并返回
    return list(set(all_tags))

